home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994…tember: Reference Library / Dev.CD Sep 94.toast / Periodicals / develop / develop Issue 18 / develop 18 code / Preferences Files / Library ƒ / StdPrefsLib.c next >
Encoding:
Text File  |  1994-03-17  |  32.9 KB  |  1,329 lines  |  [TEXT/KAHL]

  1. /*
  2.     File:        StdPrefsLib.c
  3.  
  4.     Contains:    Standard preferences library routines.
  5.  
  6.                 Refer to develop Issue 18, "The Right Way to Implement 
  7.                 Preferences Files", for additional details on this code.
  8.                 
  9.     Written by:    Gary Woodcock
  10.  
  11.     Copyright:    © 1993-94 by Apple Computer, Inc.
  12.  
  13.     Change History (most recent first):
  14.     
  15.                   3/3/94    Version 1.0.
  16.     
  17.     Notes:         This code uses Apple's Universal Interfaces for C.
  18.     
  19.                 Send bug reports to Gary Woodcock at AOL: gwoodcock
  20.                 or Internet: gwoodcock@aol.com.
  21. */
  22.  
  23. //-----------------------------------------------------------------------
  24. // Includes
  25.  
  26. #include "CompileFlags.h"
  27.  
  28. #include "StdPrefsLib.h"
  29. #include "StdPrefsLibPrivate.h"
  30. #include "DebugUtils.h"
  31. #include "FSpCompat.h"
  32. #include "MoreFilesExtras.h"
  33.  
  34. #include <Errors.h>
  35. #include <Files.h>
  36. #include <Finder.h>
  37. #include <Folders.h>
  38. #include <Memory.h>
  39. #include <Resources.h>
  40. #include <Script.h>
  41. #include <SysEqu.h>
  42. #include <ToolUtils.h>
  43.  
  44. //-----------------------------------------------------------------------
  45. // Private constants
  46.  
  47. enum
  48. {
  49.     kApplicationMissingMessageStrID = -16397
  50. };
  51.  
  52. //-----------------------------------------------------------------------
  53. // Private prototypes
  54.  
  55. static OSErr
  56. GetPrefsDirVRefNumAndDirID (ConstStr31Param folderName, 
  57.     Boolean createFolder, short *vRefNum, long *dirID);
  58.     
  59. static OSErr
  60. GetPreferencesFileFSSpec (OSType creator, OSType fileType,
  61.     FSSpec *file);
  62.  
  63. static Boolean
  64. DirIDInPrefsDir (short vRefNum, long dirID, long prefsDirID);
  65.     
  66. static short
  67. FindVRefNum (short fRefNum);
  68.  
  69. static unsigned long
  70. GetVolFreeSpace (short vRefNum);
  71.  
  72. static unsigned long
  73. GetVAllocationBlockSize (short vRefNum);
  74.  
  75. static OSErr
  76. GetDirPathName (short vRefNum, long dirID, Str255 pathName);
  77.  
  78. static OSErr
  79. CopyString (ConstStr255Param theSourceStr, Str255 theDestStr);
  80.  
  81. static OSErr
  82. CatenateStrings (ConstStr255Param theFirstStr, 
  83.     ConstStr255Param theSecondStr, Str255 theCatenatedStr);
  84.  
  85. static OSErr
  86. CopyResource (ResType theType, short theID, FSSpec *fileToCopyTo);
  87.  
  88. //-----------------------------------------------------------------------
  89.  
  90. pascal OSErr
  91. NewPreferencesFile (OSType creator, OSType fileType,
  92.     ConstStr31Param fileName, ConstStr31Param folderName, 
  93.     ConstStr31Param ownerName)
  94. {
  95.     OSErr    result = noErr;
  96.     
  97.     // Make sure parameters are valid
  98.     if (fileName != nil)
  99.     {
  100.         long    dirID;
  101.         short    vRefNum;
  102.         
  103.         // Get the vRefNum and dirID for the folder to put the preferences file in
  104.         result = GetPrefsDirVRefNumAndDirID (folderName, true, &vRefNum, &dirID);
  105.         if (result == noErr)
  106.         {
  107.             short    savedResFile = CurResFile();
  108.  
  109.             // Create the file
  110.             HCreateResFile (vRefNum, dirID, fileName);
  111.             result = ResError();
  112.             if (result == noErr)
  113.             {
  114.                 FInfo    info;
  115.                 
  116.                 // Set the file type and creator, and lock the name
  117.                 result = HGetFInfo (vRefNum, dirID, fileName, &info);
  118.                 if (result == noErr)
  119.                 {
  120.                     info.fdType = fileType;
  121.                     info.fdCreator = creator;
  122.                     info.fdFlags |= kNameLocked;
  123.                     result = HSetFInfo (vRefNum, dirID, fileName, &info);
  124.                     if (result == noErr)
  125.                     {
  126.                         // Should we add an application-missing message string?
  127.                         if (ownerName != nil)
  128.                         {
  129.                             Str255    preamble;
  130.                             Str255    postamble;
  131.                                 
  132.                             // Build the application-missing message string
  133.                             GetIndString (preamble, kStdPrefsLibStrsID, kPreambleStrID);
  134.                             GetIndString (postamble, kStdPrefsLibStrsID, kPostambleStrID);
  135.                                         
  136.                             if ((preamble != nil) && (postamble != nil))
  137.                             {
  138.                                 result = CatenateStrings (preamble, *((Str255 *)ownerName), preamble);
  139.                                 if (result == noErr)
  140.                                 {
  141.                                     result = CatenateStrings (preamble, postamble, preamble);
  142.                                     if (result == noErr)
  143.                                     {
  144.                                         short    fRefNum;
  145.                                         
  146.                                         // Open the new file
  147.                                         result = OpenPreferencesFile (creator, fileType, &fRefNum);
  148.                                         if (result == noErr)
  149.                                         {
  150.                                             Handle    messageHdl;
  151.                                             
  152.                                             // Add the application-missing message string
  153.                                             PtrToHand ((Ptr)&preamble, &messageHdl, preamble[0] + 1);
  154.                                             if ((result = MemError()) == noErr)
  155.                                             {
  156.                                                 short    resID = kApplicationMissingMessageStrID;
  157.                                                 
  158.                                                 result = WritePreference (fRefNum, 'STR ', &resID, messageHdl);
  159.                                                 DisposeHandle (messageHdl);
  160.                                                 result = MemError();
  161.                                                 FailMessage (result != noErr, NewPreferencesFile: DisposeHandle failed.);
  162.                                             }
  163.                                             else
  164.                                             {
  165.                                                 DebugMessage (NewPreferencesFile: PtrToHand failed.);
  166.                                             }
  167.                                             
  168.                                             // Close up shop
  169.                                             result = ClosePreferencesFile (fRefNum);
  170.                                         }
  171.                                     }
  172.                                 }
  173.                             }
  174.                             else    // GetIndString failed
  175.                             {
  176.                                 result = -1L;
  177.                                 DebugMessage (NewPreferencesFile: GetIndString failed.);
  178.                             }
  179.                         }
  180.                     }
  181.                     else    // HSetFInfo failed
  182.                     {
  183.                         DebugMessage (NewPreferencesFile: HSetFInfo failed.);
  184.                     }
  185.                 }
  186.                 else    // HGetFInfo failed
  187.                 {
  188.                     DebugMessage (NewPreferencesFile: HGetFInfo failed.);
  189.                 }
  190.             }
  191.             else    // HCreateResFile failed
  192.             {
  193.                 DebugMessage (NewPreferencesFile: HCreateResFile failed.);
  194.             }
  195.             
  196.             // Restore the resource file
  197.             UseResFile (savedResFile);
  198.             result = ResError();
  199.             FailMessage (result != noErr, NewPreferencesFile: UseResFile failed.);
  200.         }
  201.     }
  202.     else    // Bad parameter
  203.     {
  204.         result = paramErr;
  205.         DebugMessage (NewPreferencesFile: Bad parameter);
  206.     }
  207.     return (result);
  208. }
  209.  
  210. //-----------------------------------------------------------------------
  211.  
  212. pascal OSErr
  213. OpenPreferencesFile (OSType creator, OSType fileType, short *fRefNum)
  214. {
  215.     OSErr    result = noErr;
  216.     
  217.     // Make sure parameters are valid
  218.     if (fRefNum != nil)
  219.     {
  220.         FSSpec    prefsFSSpec;
  221.         short    savedResFile = CurResFile();
  222.         
  223.         // Get the FSSpec for the preferences file
  224.         result = GetPreferencesFileFSSpec (creator, fileType, &prefsFSSpec);
  225.         if (result == noErr)
  226.         {
  227.             // Open it up
  228.             
  229.             short    theFRefNum = FSpOpenResFileCompat (&prefsFSSpec, fsRdWrPerm);
  230.             
  231.             result = ResError();
  232.             if (result == noErr)
  233.             {    
  234.                 *fRefNum = theFRefNum;
  235.                 
  236.                 // Restore the resource file
  237.                 UseResFile (savedResFile);
  238.                 result = ResError();
  239.                 FailMessage (result != noErr, OpenPreferencesFile: UseResFile failed.);
  240.             }
  241.             else    // FSpOpenResFileCompat failed
  242.             {
  243.                 DebugMessage (OpenPreferencesFile: FSpOpenResFileCompat failed.);
  244.             }
  245.         }
  246.     }
  247.     else    // Bad parameter
  248.     {
  249.         result = paramErr;
  250.         DebugMessage (OpenPreferencesFile: Bad parameter.);
  251.     }
  252.     return (result);
  253. }
  254.  
  255. //-----------------------------------------------------------------------
  256.  
  257. pascal OSErr
  258. ClosePreferencesFile (short fRefNum)
  259. {
  260.     OSErr    result = noErr;
  261.     
  262.     // Make sure parameters are valid
  263.     if (fRefNum != -1)
  264.     {
  265.         // Close it
  266.         CloseResFile (fRefNum);
  267.         result = ResError();
  268.         FailMessage (result != noErr, ClosePreferencesFile: CloseResFile failed.);
  269.     }
  270.     else    // Bad parameter
  271.     {
  272.         result = paramErr;
  273.         DebugMessage (ClosePreferencesFile: Bad parameter.);
  274.     }
  275.     return (result);
  276. }
  277.  
  278. //-----------------------------------------------------------------------
  279.  
  280. pascal OSErr
  281. DeletePreferencesFile (OSType creator, OSType fileType)
  282. {
  283.     FSSpec    prefsFSSpec;
  284.     OSErr    result;
  285.     
  286.     // Get the preferences file FSSpec
  287.     result = GetPreferencesFileFSSpec (creator, fileType, &prefsFSSpec);
  288.     if (result == noErr)
  289.     {
  290.         // Delete it
  291.         result = FSpDeleteCompat (&prefsFSSpec);
  292.         FailMessage (result != noErr, DeletePreferencesFile: FSpDeleteCompat failed.);
  293.     }
  294.     return (result);
  295. }
  296.  
  297. //-----------------------------------------------------------------------
  298.  
  299. pascal OSErr
  300. DeletePreferencesFolder (ConstStr31Param folderName)
  301. {
  302.     OSErr    result = noErr;
  303.     
  304.     // Make sure the parameters are valid
  305.     if (folderName != nil)
  306.     {
  307.         long    dirID;
  308.         short    vRefNum;
  309.         
  310.         // Get the vRefNum and dirID of the preferences folder
  311.         result = GetPrefsDirVRefNumAndDirID (folderName, false, &vRefNum, &dirID);
  312.         if (result == noErr)
  313.         {
  314.             Str255    pathName;
  315.             
  316.             // Get the path to the folder
  317.             result = GetDirPathName (vRefNum, dirID, pathName);
  318.             if (result == noErr)
  319.             {
  320.                 // Delete it
  321.                 result = DeleteDirectory (vRefNum, dirID, (StringPtr)pathName);
  322.                 FailMessage (result != noErr, DeletePreferencesFolder: DeleteDirectory failed.);
  323.             }
  324.         }
  325.     }
  326.     else    // Bad parameter
  327.     {
  328.         result = paramErr;
  329.         DebugMessage (DeletePreferencesFolder: Bad parameter.);
  330.     }
  331.     return (result);
  332. }
  333.  
  334. //-----------------------------------------------------------------------
  335.  
  336. pascal Boolean
  337. PreferencesFileExists (OSType creator, OSType fileType)
  338. {
  339.     FSSpec    prefsFSSpec;
  340.     OSErr    result = noErr;
  341.     
  342.     // If file doesn't exist, fnfErr will be returned
  343.     result = GetPreferencesFileFSSpec (creator, fileType, &prefsFSSpec);
  344.     
  345.     return (result == noErr);
  346. }
  347.  
  348. //-----------------------------------------------------------------------
  349.  
  350. pascal OSErr
  351. GetPreferencesFileVersion (short fRefNum, short versID, 
  352.     NumVersion *numVersion, short *regionCode, 
  353.     ConstStr255Param shortVersionStr, ConstStr255Param longVersionStr)
  354. {
  355.     OSErr    result = noErr;
  356.     
  357.     // Make sure parameters are valid
  358.     if ((fRefNum != -1) && ((versID == kVers1) || (versID == kVers2)) && 
  359.         (numVersion != nil) && (regionCode != nil))
  360.     {
  361.         VersRecHndl    versHdl;
  362.         short        resID = versID;
  363.             
  364.         // Read the version resource
  365.         result = ReadPreference (fRefNum, 'vers', &resID, (Handle *)&versHdl);
  366.         if ((result == noErr) && (versHdl != nil))
  367.         {
  368.             *numVersion = (**versHdl).numericVersion;
  369.             *regionCode = (**versHdl).countryCode;
  370.             
  371.             // Note that the long version string is packed at the end of the
  372.             // short version string - this is the format for the 'vers'
  373.             // resource
  374.             BlockMoveData ((Ptr)((**versHdl).shortVersion), (Ptr)shortVersionStr,
  375.                 (**versHdl).shortVersion[0] + 1);
  376.             BlockMoveData ((Ptr)&((**versHdl).shortVersion[shortVersionStr[0] + 1]),
  377.                 (Ptr)longVersionStr, (**versHdl).shortVersion[shortVersionStr[0] + 1] + 1);
  378.                 
  379.             // Clean up
  380.             DisposeHandle ((Handle) versHdl);
  381.             result = MemError();
  382.             FailMessage (result != noErr, GetPreferencesFileVersion: DisposeHandle failed.);
  383.         }
  384.     }
  385.     else    // Bad parameter
  386.     {
  387.         result = paramErr;
  388.         DebugMessage (GetPreferencesFileVersion: Bad parameter.);
  389.     }
  390.     return (result);
  391. }
  392.  
  393. //-----------------------------------------------------------------------
  394.  
  395. pascal OSErr
  396. SetPreferencesFileVersion (short fRefNum, short versID, 
  397.     NumVersion *numVersion, short regionCode, 
  398.     ConstStr255Param shortVersionStr, ConstStr255Param longVersionStr)
  399. {
  400.     OSErr    result = noErr;
  401.     
  402.     // Make sure parameters are valid
  403.     if ((fRefNum != -1) && ((versID == kVers1) || (versID == kVers2)) && 
  404.         (numVersion != nil))
  405.     {
  406.         VersRecHndl    versHdl = (VersRecHndl) NewHandleClear (sizeof (VersRec));
  407.         
  408.         if (versHdl != nil)
  409.         {
  410.             short    resID = versID;
  411.             
  412.             (**versHdl).numericVersion = *numVersion;
  413.             (**versHdl).countryCode = regionCode;
  414.             
  415.             // Note that the long version string is packed at the end of the
  416.             // short version string - this is the format for the 'vers'
  417.             // resource
  418.             BlockMoveData ((Ptr)(shortVersionStr), (Ptr)((**versHdl).shortVersion),
  419.                 shortVersionStr[0] + 1);
  420.             BlockMoveData ((Ptr)(longVersionStr), 
  421.                 (Ptr)&((**versHdl).shortVersion[shortVersionStr[0] + 1]), 
  422.                 longVersionStr[0] + 1);
  423.                 
  424.             // Write the version resource
  425.             result = WritePreference (fRefNum, 'vers', &resID, (Handle) versHdl);
  426.         }
  427.         else    // Couldn't get any memory
  428.         {
  429.             result = MemError();
  430.             DebugMessage (SetPreferencesFileVersion: NewHandleClear failed.);
  431.         }
  432.     }
  433.     else    // Bad parameter
  434.     {
  435.         result = paramErr;
  436.         DebugMessage (SetPreferencesFileVersion: Bad parameter.);
  437.     }
  438.     return (result);
  439. }
  440.  
  441. //-----------------------------------------------------------------------
  442.  
  443. pascal OSErr
  444. ReadPreference (short fRefNum, ResType resourceType, short *resourceID, 
  445.     Handle *preference)
  446. {
  447.     OSErr    result = noErr;
  448.     
  449.     // Make sure parameters are valid
  450.     if ((fRefNum != -1) && (preference != nil))
  451.     {
  452.         short    savedResFile = CurResFile();    // Save off current resource file
  453.     
  454.         // Use this file
  455.         UseResFile (fRefNum);
  456.         result = ResError();
  457.         if (result == noErr)
  458.         {
  459.             // See if the resource is around
  460.             if ((resourceID != nil) && (*resourceID != 0))
  461.             {
  462.                 // Get the resource with the specified ID
  463.                 *preference = Get1Resource (resourceType, *resourceID);
  464.             }
  465.             else
  466.             {
  467.                 // Get the first resource of this type
  468.                 *preference = Get1IndResource (resourceType, 1);
  469.                 if ((*preference != nil) && (resourceID != nil))
  470.                 {
  471.                     Str255    dummy1;
  472.                     ResType    dummy2;
  473.                     
  474.                     // Get the resource ID
  475.                     GetResInfo (*preference, resourceID, &dummy2, dummy1);
  476.                 }
  477.             }
  478.  
  479.             // Did we get a resource successfully?
  480.             if (((result = ResError()) == noErr) && (*preference != nil))
  481.             {
  482.                 // Detach it
  483.                 DetachResource (*preference);
  484.                 result = ResError();
  485.                 FailMessage (result != noErr, ReadPreference: DetachResource failed.);
  486.             }
  487.             else    // Get1Resource failed
  488.             {    
  489.                 result = resNotFound;
  490.                 DebugMessage (ReadPreference: Get1Resource failed.);
  491.             }
  492.             
  493.             // Restore the resource file 
  494.             UseResFile (savedResFile);
  495.             result = ResError();
  496.             FailMessage (result != noErr, ReadPreference: UseResFile failed.);
  497.         }
  498.         else    // UseResFile failed
  499.         {
  500.             DebugMessage (ReadPreference: UseResFile failed.);
  501.         }
  502.     }
  503.     else    // Bad parameter
  504.     {
  505.         result = paramErr;
  506.         DebugMessage (ReadPreference: Bad parameter.);
  507.     }
  508.     return (result);
  509. }
  510.  
  511. //-----------------------------------------------------------------------
  512.  
  513. pascal OSErr
  514. WritePreference (short fRefNum, ResType resourceType, short *resourceID, 
  515.     Handle preference)
  516. {
  517.     OSErr    result = noErr;
  518.     
  519.     // Make sure parameters are valid
  520.     if ((fRefNum != -1) && (preference != nil))
  521.     {
  522.         Handle    localPref = preference;
  523.         
  524.         // Make a local copy of this preference
  525.         result = HandToHand (&localPref);
  526.         
  527.         if (result == noErr)
  528.         {
  529.             Handle    oldPref = nil;
  530.             Size    prefSize = GetHandleSize (localPref);    // Get size of preference to be added
  531.             Size    existingPrefSize = prefSize;
  532.             long    freeSpaceOnDisk;
  533.             short    vRefNum = FindVRefNum (fRefNum);
  534.             short    savedResFile = CurResFile();            // Save off current resource file
  535.             short    resID;
  536.             
  537.             // Get free disk space
  538.             freeSpaceOnDisk = GetVolFreeSpace (vRefNum);
  539.             
  540.             // Use this file
  541.             UseResFile (fRefNum);
  542.             result = ResError();
  543.             if (result == noErr)
  544.             {
  545.                 // See if this resource is already around
  546.                 if ((resourceID != nil) && (*resourceID != 0))
  547.                 {
  548.                     // Get the resource with the specified ID
  549.                     oldPref = Get1Resource (resourceType, *resourceID);
  550.                 }
  551.     
  552.                 // Did we find an existing resource?
  553.                 if (oldPref != nil)
  554.                 {
  555.                     // How big is it?
  556.                     existingPrefSize = GetHandleSize (oldPref);
  557.                     
  558.                     // Make sure that there's enough disk space available to
  559.                     // accomodate the resource that's replacing the old one -
  560.                     // if there's not, leave the old one in place, and return an error
  561.                     // We'll leave at least one allocation block left for the system
  562.                     if ((prefSize > existingPrefSize) && 
  563.                         (prefSize + GetVAllocationBlockSize (FindVRefNum (fRefNum)) > freeSpaceOnDisk))
  564.                     {
  565.                         result = dskFulErr;
  566.                         DebugMessage (WritePreference: Not enough disk space to write resource.);
  567.                     }
  568.                     else
  569.                     {
  570.                         // Get rid of it
  571.                         RmveResource (oldPref);
  572.                         if ((result = ResError()) == noErr)
  573.                         {
  574.                             // Update the file
  575.                             UpdateResFile (fRefNum);
  576.                             result = ResError();
  577.                             FailMessage (result != noErr, WritePreference: UpdateResFile failed.);
  578.                         }
  579.                         else    // RmveResource failed
  580.                         {
  581.                             DebugMessage (WritePreference: RmveResource failed.);
  582.                         }
  583.                         
  584.                         // Clean up
  585.                         DisposeHandle (oldPref);
  586.                         result = MemError();
  587.                         FailMessage (result != noErr, WritePreference: DisposeHandle failed.);
  588.                     }
  589.                 }
  590.                 
  591.                 if (result == noErr)
  592.                 {
  593.                     // Make sure there's room
  594.                     if (prefSize + GetVAllocationBlockSize (FindVRefNum (fRefNum)) > freeSpaceOnDisk)
  595.                     {
  596.                         result = dskFulErr;
  597.                         DebugMessage (WritePreference: Not enough disk space to write resource.);
  598.                     }
  599.                     else
  600.                     {
  601.                         // Figure out the resource ID to use
  602.                         if (resourceID != nil)
  603.                         {
  604.                             if (*resourceID == 0)
  605.                             {
  606.                                 // Get a unique resource ID
  607.                                 resID = Unique1ID (resourceType);
  608.                                 *resourceID = resID;
  609.                             }
  610.                             else
  611.                             {
  612.                                 resID = *resourceID;
  613.                             }
  614.                         }
  615.                         else
  616.                         {
  617.                             // Get a unique resource ID
  618.                             resID = Unique1ID (resourceType);
  619.                         }
  620.             
  621.                         // Add the resource
  622.                         AddResource (localPref, resourceType, resID, "\p");
  623.                         if ((result = ResError()) == noErr)
  624.                         {
  625.                             // Write it to the file
  626.                             WriteResource (localPref);
  627.                             FailMessage ((result = ResError()) != noErr, WritePreference: 
  628.                                 WriteResource failed.);
  629.                                 
  630.                             // Clean up
  631.                             ReleaseResource (localPref);
  632.                             FailMessage ((result = ResError()) != noErr, WritePreference: 
  633.                                 ReleaseResource failed.);
  634.                         }
  635.                         else    // AddResource failed
  636.                         {
  637.                             DebugMessage (WritePreference: AddResource failed.);
  638.                         }
  639.                     }
  640.                 }
  641.                 
  642.                 // Restore the resource file
  643.                 UseResFile (savedResFile);
  644.                 result = ResError();
  645.                 FailMessage (result != noErr, WritePreference: UseResFile failed.);
  646.             }
  647.             else    // UseResFile failed
  648.             {
  649.                 DebugMessage (WritePreference: UseResFile failed.);
  650.             }
  651.         }
  652.         else    // HandToHand failed
  653.         {
  654.             DebugMessage (WritePreference: HandToHand failed.);
  655.         }
  656.     }
  657.     else    // Bad parameter
  658.     {
  659.         result = paramErr;
  660.         DebugMessage (WritePreference: Bad parameter.);
  661.     }
  662.     return (result);
  663. }
  664.  
  665. //-----------------------------------------------------------------------
  666.  
  667. pascal OSErr
  668. DeletePreference (short fRefNum, ResType resourceType, short resourceID)
  669. {
  670.     OSErr    result = noErr;
  671.     
  672.     // Make sure parameters are valid
  673.     if (fRefNum != -1)
  674.     {
  675.         Handle    prefToKill;
  676.         short    savedResFile = CurResFile();    // Save off the current resource file
  677.         
  678.         // Use this file
  679.         UseResFile (fRefNum);
  680.         result = ResError();
  681.         if (result == noErr)
  682.         {
  683.             // Are there any of these resources around?
  684.             if (Count1Resources (resourceType) > 0)
  685.             {
  686.                 // See if this resource is around
  687.                 if (resourceID != 0)
  688.                 {
  689.                     // Get the resource with the specified ID
  690.                     prefToKill = Get1Resource (resourceType, resourceID);
  691.                 }
  692.                 else
  693.                 {
  694.                     // Get the first resource of this type
  695.                     prefToKill = Get1IndResource (resourceType, 1);
  696.                 }
  697.                 
  698.                 // Did we find a resource to delete?
  699.                 if (prefToKill != nil)
  700.                 {
  701.                     // Remove it
  702.                     RmveResource (prefToKill);
  703.                     if ((result = ResError()) == noErr)
  704.                     {
  705.                         // Update the file
  706.                         UpdateResFile (fRefNum);
  707.                         FailMessage ((result = ResError()) != noErr, DeletePreference: 
  708.                             UpdateResFile failed.);
  709.                     }
  710.                     else    // RmveResource failed
  711.                     {
  712.                         DebugMessage (DeletePreference: RmveResource failed.);
  713.                     }
  714.                     
  715.                     // Clean up
  716.                     DisposeHandle (prefToKill);
  717.                     result = MemError();
  718.                     FailMessage (result != noErr, DeletePreference: DisposeHandle failed.);
  719.                 }
  720.             }
  721.             else    // Couldn't find this resource
  722.             {
  723.                 result = resNotFound;
  724.                 DebugMessage (DeletePreference: Resource not found.);
  725.             }
  726.             
  727.             // Restore the resource file
  728.             UseResFile (savedResFile);
  729.             result = ResError();
  730.             FailMessage (result != noErr, DeletePreference: UseResFile failed.);
  731.         }
  732.         else    // UseResFile failed
  733.         {
  734.             DebugMessage (DeletePreference: UseResFile failed.);
  735.         }
  736.     }
  737.     else    // Bad parameter
  738.     {
  739.         result = paramErr;
  740.         DebugMessage (DeletePreference: Bad parameter.);
  741.     }
  742.     return (result);
  743. }
  744.  
  745. //-----------------------------------------------------------------------
  746.  
  747. static OSErr
  748. GetPrefsDirVRefNumAndDirID (ConstStr31Param folderName, 
  749.     Boolean createFolder, short *vRefNum, long *dirID)
  750. {
  751.     OSErr    result = noErr;
  752.     
  753.     // Make sure parameters are valid
  754.     if ((vRefNum != nil) && (dirID != nil))
  755.     {
  756.         long    id;
  757.         
  758.         // Locate the Preferences folder
  759.         result = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
  760.             vRefNum, &id);
  761.         if (result == fnfErr)
  762.         {
  763.             // No Preferences folder found, try to find the System Folder
  764.             result = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
  765.                 vRefNum, &id);
  766.             if (result != noErr)
  767.             {
  768.                 // Couldn't find the System Folder - we're hosed
  769.                 DebugMessage (GetPrefsDirVRefNumAndDirID: FindFolder failed.);
  770.                 goto BAIL;
  771.             }
  772.         }
  773.         else if (result != noErr)
  774.         { 
  775.             // Got some other kind of bad error from FindFolder when trying
  776.             // to find the Preferences folder
  777.             DebugMessage (GetPrefsDirVRefNumAndDirID: FindFolder failed.);
  778.             goto BAIL;
  779.         }
  780.         
  781.         // Are we using a custom preferences folder?
  782.         if (folderName != nil)
  783.         {
  784.             DirInfo    info;
  785.             
  786.             // See if the specified custom preferences folder is around
  787.             info.ioNamePtr = (StringPtr) folderName;
  788.             info.ioVRefNum = *vRefNum;
  789.             info.ioDrDirID = id;
  790.             info.ioFDirIndex = 0;
  791.             result = PBGetCatInfo ((CInfoPBPtr)&info, false);
  792.             if (result == noErr)
  793.             {
  794.                 *dirID = info.ioDrDirID;
  795.             }
  796.             else if ((result == fnfErr) && createFolder)
  797.             {
  798.                 // Try to create the specified custom preferences folder
  799.                 result = DirCreate (*vRefNum, id, folderName, dirID);
  800.                 FailMessage (result != noErr, GetPrefsDirVRefNumAndDirID: DirCreate failed.);
  801.             }
  802.             else    // PBGetCatInfo failed
  803.             {
  804.                 DebugMessage (GetPrefsDirVRefNumAndDirID: PBGetCatInfo failed.);
  805.             }
  806.         }
  807.         else    // Not using a custom preferences folder
  808.         {
  809.             *dirID = id;
  810.         }
  811.     }
  812.     else    // Bad parameter
  813.     {
  814.         result = paramErr;
  815.         DebugMessage (GetPrefsDirVRefNumAndDirID: Bad parameter.);
  816.     }
  817.     
  818. BAIL:
  819.     return (result);
  820. }
  821.  
  822. //-----------------------------------------------------------------------
  823.  
  824. static OSErr
  825. GetPreferencesFileFSSpec (OSType creator, OSType fileType,
  826.     FSSpec *file)
  827. {
  828.     OSErr    result = noErr;
  829.     
  830.     // Make sure parameters are valid
  831.     if (file != nil)
  832.     {
  833.         long    systemFolderDirID;
  834.         long    prefsFolderDirID;
  835.         short    vRefNum;
  836.         Boolean    hasPrefsDir;
  837.         Boolean    foundIt = false;
  838.         
  839.         // Find the Preferences folder dirID
  840.         result = FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
  841.             &vRefNum, &prefsFolderDirID);
  842.         hasPrefsDir = (result == noErr);
  843.         
  844.         // Find the System Folder dirID
  845.         result = FindFolder (kOnSystemDisk, kSystemFolderType, kDontCreateFolder,
  846.             &vRefNum, &systemFolderDirID);
  847.             
  848.         // Did we find the System Folder OK?
  849.         if (result == noErr)
  850.         {
  851.             FSSpec    possibleMatch;
  852.             long    numMatches;
  853.             Boolean    firstSearch = true;
  854.             
  855.             // Check the Preferences folder first
  856.             if (hasPrefsDir)
  857.             {
  858.                 // Loop through the possible candidates until a candidate is found
  859.                 do
  860.                 {
  861.                     result = CreatorTypeFileSearch (nil, vRefNum, creator, fileType, 
  862.                         (FSSpecPtr) &possibleMatch, 1L, &numMatches, firstSearch);
  863.                         
  864.                     firstSearch = false;
  865.                     
  866.                     // Did we get a match?
  867.                     if (numMatches == 1)
  868.                     {
  869.                         // Check to see if this file is within the Preferences
  870.                         // folder or any of its nested folders
  871.                         if (DirIDInPrefsDir (vRefNum, possibleMatch.parID, prefsFolderDirID))
  872.                         {
  873.                             // Got one!
  874.                             foundIt = true;
  875.                             break;
  876.                         }
  877.                     }
  878.                 }
  879.                 while ((result == noErr) || (result == catChangedErr));
  880.             }
  881.             
  882.             // If we didn't find it in the Preferences folder, check the
  883.             // System Folder
  884.             if (!foundIt)
  885.             {
  886.                 // Loop through the possible candidates until a candidate is found
  887.                 firstSearch = true;
  888.                 do
  889.                 {
  890.                     result = CreatorTypeFileSearch (nil, vRefNum, creator, fileType, 
  891.                         (FSSpecPtr) &possibleMatch, 1L, &numMatches, firstSearch);
  892.                         
  893.                     firstSearch = false;
  894.                     
  895.                     // Did we get a match?
  896.                     if ((numMatches == 1) && (possibleMatch.parID == systemFolderDirID))
  897.                     {
  898.                         // Got one!
  899.                         foundIt = true;
  900.                         break;
  901.                     }
  902.                 }
  903.                 while ((result == noErr) || (result == catChangedErr));
  904.             }
  905.             
  906.             // If we found one, return it
  907.             if (foundIt)
  908.             {
  909.                 *file = possibleMatch;
  910.             }
  911.             else    // Didn't find a preferences file with the specified creator and file type
  912.             {
  913.                 result = fnfErr;
  914.                 DebugMessage (GetPreferencesFileFSSpec: Could not find preferences file.);
  915.             }
  916.         }
  917.         else    // Couldn't find the System Folder
  918.         {
  919.             DebugMessage (GetPreferencesFileFSSpec: FindFolder failed.);
  920.         }
  921.     }
  922.     else    // Bad parameter
  923.     {
  924.         result = paramErr;
  925.         DebugMessage (GetPreferencesFileFSSpec: Bad parameter.);
  926.     }
  927.     return (result);
  928. }
  929.  
  930. //-----------------------------------------------------------------------
  931.  
  932. static Boolean
  933. DirIDInPrefsDir (short vRefNum, long dirID, long prefsDirID)
  934. {
  935.     OSErr    result = noErr;
  936.     Boolean    inPrefsDir = false;
  937.     
  938.     // Make sure parameters are valid
  939.     if (vRefNum < 0)
  940.     {
  941.         // Is the dirID of the directory the same as the Preferences 
  942.         // folder dirID?
  943.         if (dirID == prefsDirID)
  944.         {
  945.             inPrefsDir = true;
  946.         }
  947.         else    // Nope, we need to follow the path up toward the root
  948.         {
  949.             DirInfo    info;
  950.             Str255    dirName;
  951.             Str255    pathName;
  952.             
  953.             // Initialize some stuff
  954.             
  955.             pathName[0] = '\0';
  956.         
  957.             info.ioDrParID = dirID;
  958.             info.ioNamePtr = dirName;
  959.             
  960.             // Walk the path upward, one directory at a time,
  961.             // until we either determine that the directory is
  962.             // in the Preferences folder, or until we hit the
  963.             // root directory (this always has a dirID of 2)
  964.             do
  965.             {
  966.                 info.ioVRefNum = vRefNum;
  967.                 info.ioFDirIndex = -1;
  968.                 info.ioDrDirID = info.ioDrParID;
  969.                 result = PBGetCatInfo ((CInfoPBPtr)&info, false);
  970.                 if (result == noErr)
  971.                 {
  972.                     // Is this the Preferences folder?
  973.                     if (info.ioDrParID == prefsDirID)
  974.                     {
  975.                         // Yep, we can leave now
  976.                         inPrefsDir = true;
  977.                         break;
  978.                     }
  979.                     else    // Nope, append this string to the path name and try again
  980.                     {
  981.                         result = CatenateStrings (dirName, "\p:", dirName);
  982.                         if (result == noErr)
  983.                         {
  984.                             result = CatenateStrings (dirName, pathName, pathName);
  985.                             if (result != noErr)
  986.                             {
  987.                                 // CatenateStrings failed
  988.                                 break;
  989.                             }
  990.                         }
  991.                         else    // CatenateStrings failed
  992.                         {
  993.                             break;
  994.                         }
  995.                     }
  996.                 }
  997.                 else    // PBGetCatInfo failed
  998.                 {
  999.                     DebugMessage (DirIDInPrefsDir: PBGetCatInfo failed.);
  1000.                     break;
  1001.                 }
  1002.             }
  1003.             while (info.ioDrDirID != 2);
  1004.         }
  1005.     }
  1006.     else    // Bad parameter
  1007.     {
  1008.         DebugMessage (DirIDInPrefsDir: Bad parameter.);
  1009.     }
  1010.     return (inPrefsDir);
  1011. }
  1012.  
  1013. //-----------------------------------------------------------------------
  1014.  
  1015. static short
  1016. FindVRefNum (short fRefNum)
  1017. {
  1018.     OSErr    result = noErr;
  1019.     short    vRefNum = 0;
  1020.     
  1021.     // Make sure parameters are valid
  1022.     if (fRefNum != -1)
  1023.     {
  1024.         FCBPBRec    fcbInfo;
  1025.         Str63        fName;
  1026.     
  1027.         // Set up parameter block
  1028.         fcbInfo.ioNamePtr = fName;
  1029.         fcbInfo.ioVRefNum = 0;
  1030.         fcbInfo.ioRefNum = fRefNum;
  1031.         fcbInfo.ioFCBIndx = 0;
  1032.         result = PBGetFCBInfoSync (&fcbInfo);
  1033.         if (result == noErr)
  1034.         {
  1035.             vRefNum = fcbInfo.ioFCBVRefNum;
  1036.         }
  1037.         else    // PBGetFCBInfoSync failed
  1038.         {
  1039.             DebugMessage (FindVRefNum: PBGetFCBInfoSync failed.);
  1040.         }
  1041.     }
  1042.     else    // Bad parameter
  1043.     {
  1044.         DebugMessage (FindVRefNum: Bad parameter.);
  1045.     }
  1046.     return (vRefNum);
  1047. }
  1048.  
  1049. //-----------------------------------------------------------------------
  1050.  
  1051. static unsigned long
  1052. GetVolFreeSpace (short vRefNum)
  1053. {
  1054.     HParamBlockRec    pb;
  1055.     OSErr            result = noErr;
  1056.  
  1057.     pb.volumeParam.ioNamePtr = nil;            // We don't care about the name
  1058.     pb.volumeParam.ioVRefNum = vRefNum;
  1059.     pb.volumeParam.ioVolIndex = 0;            // Use ioVRefNum only
  1060.     result = PBHGetVInfo (&pb, false);
  1061.  
  1062.     if (result == noErr)
  1063.     {
  1064.         // Calculate the free space in bytes
  1065.         return (pb.volumeParam.ioVFrBlk * pb.volumeParam.ioVAlBlkSiz);
  1066.     }
  1067.     else    // PBHGetVInfo failed
  1068.     {
  1069.         DebugMessage (GetVolFreeSpace: PBHGetVInfo failed.);
  1070.         return (0L);
  1071.     }
  1072. }
  1073.  
  1074. //-----------------------------------------------------------------------
  1075.  
  1076. static unsigned long
  1077. GetVAllocationBlockSize (short vRefNum)
  1078. {
  1079.     HParamBlockRec    pb;
  1080.     OSErr            result = noErr;
  1081.     
  1082.     pb.volumeParam.ioNamePtr = nil;        // We don't care about the name
  1083.     pb.volumeParam.ioVRefNum = vRefNum;    
  1084.     pb.volumeParam.ioVolIndex = 0;        // Use ioVRefNum only
  1085.     result = PBHGetVInfo (&pb, false);
  1086.     
  1087.     if (result == noErr)
  1088.     {
  1089.         return (pb.volumeParam.ioVAlBlkSiz);
  1090.     }
  1091.     else
  1092.     {
  1093.         return (0L);
  1094.     }
  1095. }
  1096.  
  1097. //-----------------------------------------------------------------------
  1098.  
  1099. static OSErr
  1100. GetDirPathName (short vRefNum, long dirID, Str255 pathName)
  1101. {
  1102.     OSErr    result = noErr;
  1103.     
  1104.     // Make sure parameters are valid
  1105.     if (vRefNum < 0)
  1106.     {
  1107.         DirInfo    info;
  1108.         Str255    dirName;
  1109.         
  1110.         // Initialize some stuff
  1111.         
  1112.         pathName[0] = '\0';
  1113.     
  1114.         info.ioDrParID = dirID;
  1115.         info.ioNamePtr = dirName;
  1116.         
  1117.         // Walk the path upward, one directory at a time,
  1118.         // until we hit the root directory (this always 
  1119.         // has a dirID of 2)
  1120.         do
  1121.         {
  1122.             info.ioVRefNum = vRefNum;
  1123.             info.ioFDirIndex = -1;
  1124.             info.ioDrDirID = info.ioDrParID;
  1125.             result = PBGetCatInfo ((CInfoPBPtr)&info, false);
  1126.             if (result == noErr)
  1127.             {
  1128.                 result = CatenateStrings (dirName, "\p:", dirName);
  1129.                 if (result == noErr)
  1130.                 {
  1131.                     result = CatenateStrings (dirName, pathName, pathName);
  1132.                     if (result != noErr)
  1133.                     {
  1134.                         // CatenateStrings failed
  1135.                         break;
  1136.                     }
  1137.                 }
  1138.                 else    // CatenateStrings failed
  1139.                 {
  1140.                     break;
  1141.                 }
  1142.             }
  1143.             else    // PBGetCatInfo failed
  1144.             {
  1145.                 DebugMessage (GetDirPathName: PBGetCatInfo failed.);
  1146.                 break;
  1147.             }
  1148.         }
  1149.         while (info.ioDrDirID != 2);
  1150.     }
  1151.     else    // Bad parameter
  1152.     {
  1153.         result = paramErr;
  1154.         DebugMessage (GetDirPathName: Bad parameter.);
  1155.     }
  1156.     return (result);
  1157. }
  1158.  
  1159. //-----------------------------------------------------------------------
  1160.  
  1161. static OSErr
  1162. CopyString (ConstStr255Param theSourceStr, Str255 theDestStr)
  1163. {
  1164.     OSErr    result = noErr;
  1165.  
  1166.     // Make sure parameters are valid
  1167.     if ((theSourceStr != nil) && (theDestStr != nil))
  1168.     {
  1169.         BlockMoveData ((Ptr)theSourceStr, (Ptr)theDestStr, theSourceStr[0] + 1);
  1170.     }
  1171.     else    // Bad parameter
  1172.     {
  1173.         result = paramErr;
  1174.         DebugMessage (StrCopy: Bad parameter.);
  1175.     }
  1176.     return (result);
  1177. }
  1178.  
  1179. //-----------------------------------------------------------------------
  1180.  
  1181. static OSErr
  1182. CatenateStrings (ConstStr255Param theFirstStr,
  1183.     ConstStr255Param theSecondStr, Str255 theCatenatedStr)
  1184. {
  1185.     OSErr    result = noErr;
  1186.     
  1187.     // Make sure parameters are valid
  1188.     if ((theFirstStr != nil) && (theSecondStr != nil) && 
  1189.         (theCatenatedStr != nil))
  1190.     {
  1191.         Str255    catenatedStr;
  1192.         Str255    secondStr;
  1193.         long    tempSize;
  1194.         short    combinedLength = theFirstStr[0] + theSecondStr[0];
  1195.         short    secondStrLength = theSecondStr[0];
  1196.         
  1197.         // Can both strings fit in a Str255?
  1198.         if (combinedLength > 255)
  1199.         {
  1200.             // Nope, let's shorten the second string to fit
  1201.             secondStrLength = 255 - theFirstStr[0];
  1202.         }
  1203.  
  1204.         // Copy the strings
  1205.         BlockMoveData ((Ptr)theFirstStr, (Ptr)catenatedStr, theFirstStr[0] + 1);
  1206.         BlockMoveData ((Ptr)theSecondStr, (Ptr)secondStr, secondStrLength + 1);
  1207.     
  1208.         // Copy the second string to the end of the first string
  1209.         tempSize = catenatedStr[0];
  1210.         catenatedStr[0] += secondStr[0];
  1211.         BlockMoveData ((Ptr)&secondStr[1], (Ptr)&catenatedStr[tempSize + 1], 
  1212.             secondStr[0]);
  1213.         
  1214.         // Copy the catenated string back out
  1215.         BlockMoveData ((Ptr)catenatedStr, (Ptr)theCatenatedStr, 
  1216.             catenatedStr[0] + 1);
  1217.     }
  1218.     else    // Bad parameter
  1219.     {
  1220.         result = paramErr;
  1221.         DebugMessage (StrCatenate: Bad parameter.);
  1222.     }
  1223.     return (result);
  1224. }
  1225.  
  1226. //-----------------------------------------------------------------------
  1227.  
  1228. static OSErr
  1229. CopyResource (ResType theType, short theID, FSSpec *fileToCopyTo)
  1230. {
  1231.     OSErr    result = noErr;
  1232.     
  1233.     // Make sure parameters are valid
  1234.     if (fileToCopyTo != nil)
  1235.     {
  1236.         Handle    resToBeCopied = GetResource (theType, theID);
  1237.         short    appResRefNum;
  1238.         
  1239.         if ((result = ResError()) == noErr)
  1240.         {
  1241.             // Get the application's resource refNum
  1242.             appResRefNum = LMGetCurApRefNum();
  1243.             if ((resToBeCopied != nil) &&
  1244.                 (appResRefNum == HomeResFile (resToBeCopied)))
  1245.             {
  1246.                 Handle    resToCopyTo;
  1247.                 short    resAttributes;
  1248.                 short    copyToFileResRefNum;
  1249.                 short    savedResFile = CurResFile();
  1250.                 
  1251.                 // Get the attributes of the resource to be copied
  1252.                 resAttributes = GetResAttrs (resToBeCopied);
  1253.                 
  1254.                 // Detach the resource to be coped
  1255.                 DetachResource (resToBeCopied);
  1256.                 if ((result = ResError()) == noErr)
  1257.                 {
  1258.                     // Open the file to copy to
  1259.                     copyToFileResRefNum = FSpOpenResFile (fileToCopyTo, fsRdWrPerm);
  1260.                     if ((result = ResError()) == noErr)
  1261.                     {
  1262.                         // Set the resource refNum
  1263.                         UseResFile (copyToFileResRefNum);
  1264.                         
  1265.                         // Check for existing resources with this type and ID in
  1266.                         // the file we're copying to, and remove any we find
  1267.                         do
  1268.                         {
  1269.                             // Get a resource
  1270.                             resToCopyTo = GetResource (theType, theID);
  1271.  
  1272.                             // Is the resource's res file the same as the
  1273.                             // file we're trying to copy to?
  1274.                             if (HomeResFile (resToCopyTo) == copyToFileResRefNum)
  1275.                             {
  1276.                                 // Remove it
  1277.                                 RmveResource (resToCopyTo);
  1278.                                 FailMessage ((result = ResError()) != noErr, CopyResource: RmveResource failed.);
  1279.                                 DisposeHandle (resToCopyTo);
  1280.                                 FailMessage ((result = MemError()) != noErr, CopyResource: DisposeHandle failed.);
  1281.                             }
  1282.                         }
  1283.                         while (HomeResFile (resToCopyTo) != copyToFileResRefNum);
  1284.                         
  1285.                         // Add the resource
  1286.                         AddResource (resToBeCopied, theType, theID, "\p");
  1287.                         if ((result = ResError()) == noErr)
  1288.                         {
  1289.                             // Set up the resource's attributes
  1290.                             SetResAttrs (resToBeCopied, resAttributes);
  1291.                             ChangedResource (resToBeCopied);
  1292.                             FailMessage ((result = ResError()) != noErr, CopyResource: ChangedResource failed.);
  1293.                         }
  1294.                         
  1295.                         // Close the file
  1296.                         CloseResFile (copyToFileResRefNum);
  1297.                         
  1298.                         // Restore resource file
  1299.                         UseResFile (savedResFile);
  1300.                     }
  1301.                     else    // FSpOpenResFile failed
  1302.                     {
  1303.                         DebugMessage (CopyResource: FSpOpenResFile failed.);
  1304.                     }
  1305.                 }
  1306.                 else    // DetachResource failed
  1307.                 {
  1308.                     DebugMessage (CopyResource: DetachResource failed.);
  1309.                 }
  1310.             }
  1311.         }
  1312.         else    // GetResource failed
  1313.         {
  1314.             DebugMessage (CopyResource: GetResource failed.);
  1315.         }
  1316.     }
  1317.     else    // Bad parameter
  1318.     {
  1319.         result = paramErr;
  1320.         DebugMessage (CopyResource: Bad parameter.);
  1321.     }
  1322.     return (result);
  1323. }
  1324.  
  1325. //-----------------------------------------------------------------------
  1326.  
  1327.  
  1328.  
  1329.